/*
 
 Author: rjkyng.
 
 Compatibility: Tested on Mac, should run on Windows, may not run on Linux.
 
 Attribution: This uses code bits from the book Learning OpenCV.
 
 LOCV notes:
 
	 License:
	 Oct. 3, 2008
	 Right to use this code in any way you want without warrenty, support or any guarentee of it working.
	 
	 Learning OpenCV: Computer Vision with the OpenCV Library
	 by Gary Bradski and Adrian Kaehler
	 Published by O'Reilly Media, October 3, 2008

*/

#include <highgui.h>
#include <iostream>
#include <cv.h>

//Declarations
//-- see definitions below for documentation

void mouse_callback_TagAndNextFrame( int event, int x, int y, int flags, void* param);

void cleanup();

void storeData( char* inputFileName, CvCapture* capture, int tagCount, CvSeq* pointTagSequence  );

char* makeTagSeqXMLNameString( char* inputFileName ); //buffer overflow!?

void deAllocateTagSeqXMLNameString( char* tagSeqXMLNameString );


//Definitions

//Variables

//Horrible dependencies on global variables here! How I wish I could use Java and have nice GC.
CvCapture* capture;
IplImage* frame;
char* windowName = "AVI Frame Tagger";

//For storing the sequence of point tags made by the user
CvMemStorage* storage_handLocations;

CvSeq* handLocations;


//Defining the callback function to install for mouse events

void mouse_callback_TagAndNextFrame( int event, int x, int y, int flags, void* tagCountRef) {
	
	switch (event) {
		case CV_EVENT_LBUTTONUP:
			if (frame != NULL) {
				std::cout << "x = " << x << "; y = " << y << std::endl; //print click location
				
				CvPoint p = cvPoint(x,y);
				
				cvSeqPush( handLocations, &p ); //store the click location
				
				* ((int*) tagCountRef) = * ((int*) tagCountRef) + 1;
				
				frame = cvQueryFrame( capture ); //get next frame
				
				if (frame == NULL) {
					std::cout << "No frames left. Press ESC to exit" << std::endl;
				} else {
					cvShowImage( windowName, frame ); //show next frame
				}
			}
			break;
		default:
			break;
	}
	
}

void cleanup() {
	cvReleaseCapture( &capture );
	cvDestroyWindow( windowName );
	
	cvClearSeq( handLocations );
	cvReleaseMemStorage( &storage_handLocations );
}

void storeData( char* inputFileName, CvCapture* capture, int tagCount, CvSeq* pointTagSequence  ) {
	
	int frameCount = (int) cvGetCaptureProperty(
											capture,
											CV_CAP_PROP_FRAME_COUNT
											);
    
    int w = (int) cvGetCaptureProperty(
										  capture,
										  CV_CAP_PROP_FRAME_WIDTH
										  );
	
    int h = (int) cvGetCaptureProperty(
										  capture,
										  CV_CAP_PROP_FRAME_HEIGHT
										  );
	
	char* tagSeqXMLNameString = makeTagSeqXMLNameString( inputFileName );
	
	CvFileStorage* fs = cvOpenFileStorage(
										  tagSeqXMLNameString,
										  0,
										  CV_STORAGE_WRITE
										  );
	cvWriteInt( fs, "frame_count", frameCount );
	cvWriteInt( fs, "tag_count", tagCount );
	cvStartWriteStruct( fs, "frame_size", CV_NODE_SEQ); //ORDER: W, H !
	cvWriteInt( fs, 0, w );
	cvWriteInt( fs, 0, h );
	cvEndWriteStruct(fs);
	cvWrite( fs, "point_tag_sequence", pointTagSequence );
	cvReleaseFileStorage( &fs );
	
	deAllocateTagSeqXMLNameString( tagSeqXMLNameString );													 
}

char* makeTagSeqXMLNameString( char* inputFileName ) {
	
	char* tagSeqXMLSuffix = ".pointtags.xml";
	
	char* tagSeqXMLNameString = (char*) malloc(strlen(inputFileName) + strlen(tagSeqXMLSuffix) + 1); //+1 for null at the end
	
	strcpy(tagSeqXMLNameString, inputFileName);
	strcat(tagSeqXMLNameString, tagSeqXMLSuffix);
	
	return tagSeqXMLNameString;
}

void deAllocateTagSeqXMLNameString( char* tagSeqXMLNameString ) {
	
	free(tagSeqXMLNameString);
	
}
	


int main( int argc, char** argv ) {
	
	char* inputFileName = argv[1];
	
	cvNamedWindow( windowName, CV_WINDOW_AUTOSIZE );
	
	storage_handLocations = cvCreateMemStorage();
	handLocations = cvCreateSeq( CV_SEQ_ELTYPE_POINT | CV_SEQ_KIND_CURVE, sizeof(CvSeq), sizeof(CvPoint), storage_handLocations);
	
	capture = cvCreateFileCapture( inputFileName );
	frame = cvQueryFrame( capture );
	
	if( !frame ) {
		std::cout << "No frames found" << std::endl;
		cleanup();
		return 0;
	}
	cvShowImage( windowName, frame ); //show the first frame so user may begin tagging
	
	int tagCount = 0; //for counting the number of tags made

	cvSetMouseCallback( windowName, mouse_callback_TagAndNextFrame, &tagCount );
		//set mouse callback to TagAndNextFrame.
		//give pointer to tagCount so value can be incremented
	
	while(1) {
		char c = cvWaitKey(0);
		if( c == 27 ) break; //exit if user presses escape
	}
	
	//TODO extract generic yes/no prompt? Could even use factory.
	
	std::cout << "Would you like to store the sequence of point tags? (y or n)" << std::endl;
	
	char ans;
	
	std::cin >> ans;
	
	std::cout << ans << std::endl;
	
	bool requestInput = true;
	
	while( requestInput ) {
		switch (ans) {
			case 121: //ASCII y = 121
				storeData( inputFileName, capture, tagCount, handLocations ); 
				requestInput = false;
				break;
			case 110: //ASCII y = 121
				requestInput = false;
				break;	
			default:
				std::cout << "Input not recognised. Try again." << std::endl;
				std::cin >> ans;
				break;
		}
	}
	
	cleanup();
	return 0;
}